package util

import (
    "os"
    "bufio"
    "io"
    "strings"
    "strconv"
    "errors"
    "log"
)

type Obj3D struct {

    //文件包含的多个obj模型
    CountObj int

    //顶点法线(Vertex normals)
    Vn       [][]float32


    //几何体顶点(Geometric vertices)
    V        [][]float32

    //贴图坐标点(Texture vertices)
    Vt       [][]float32

    //参数空格顶点 (Parameter space vertices)
    Vp       [][]float32



    //"f 顶点索引/uv点索引/法线索引"  贴图坐标uv点和法线的索引号，索引号分别用左斜线(/)隔开
    FVIndex  [][]int32
    FUVIndex [][]int32
    FNIndex  [][]int32


}

func (m *Obj3D)appendIndex(data string)(err error){
    var dataInt int64
    faceData := strings.Split(data, "/")
    for j := 0; j < len(faceData); j++ {

        if(faceData[j]==""){
            continue
        }
        if dataInt, err = strconv.ParseInt(faceData[j], 10, 32); err != nil {
            return
        }
        if (j == 0) {
            m.FVIndex[m.CountObj - 1] = append(m.FVIndex[m.CountObj - 1], int32(dataInt - 1))
        }
        if (j == 1) {
            m.FUVIndex[m.CountObj - 1] = append(m.FUVIndex[m.CountObj - 1], int32(dataInt - 1))
        }
        if (j == 2) {
            m.FNIndex[m.CountObj - 1] = append(m.FNIndex[m.CountObj - 1], int32(dataInt - 1))
        }

    }
    err = nil
    return
}

//解析文件中的各种数据
func (m *Obj3D)parseData(filePath string)(err error){
    var parseFile *os.File

    parseFile, err = os.Open(filePath)
    if err != nil{
        return
    }
    defer parseFile.Close()
    hasNew := true
    m.CountObj = 0
    m.V = make([][]float32, 0, 10)

    m.Vn = make([][]float32, 0, 10)

    m.Vp = make([][] float32, 0, 10)

    m.Vt = make([][]float32, 0, 10)

    m.FUVIndex = make([][]int32, 0, 10)

    m.FNIndex = make([][]int32, 0, 10)

    m.FVIndex = make([][]int32, 0, 10)



    buf := bufio.NewReader(parseFile)
    lineCount := 0
    var line string

    for {
        lineCount += 1
        line, err = buf.ReadString('\n')
        if err != nil {
            //如果是文件已读取完，不算错误
            if err == io.EOF {
                err = nil
            }
            break
        }

        line = strings.TrimSpace(line)

        firstItem := strings.Split(line, " ")[0]

        var dataFloat float64
        switch firstItem {
        case "v":
            other_items := strings.Split(strings.TrimSpace(line[1:]), " ")
            if len(other_items)!= 3{
                err = errors.New("v length error")
                return
            }
            if hasNew{
                m.CountObj +=1
                m.V = append(m.V, make([]float32, 0, 1024*6))

                m.Vn = append(m.Vn, make([]float32, 0, 1024*6))

                m.Vt = append(m.Vt, make([]float32, 0, 1024*6))

                m.Vp = append(m.Vp,  make([]float32, 0, 1024*6))

                m.FUVIndex = append(m.FUVIndex, make([]int32, 0, 1024*6))

                m.FNIndex = append(m.FNIndex, make([]int32, 0, 1024*6))

                m.FVIndex = append(m.FVIndex,make([]int32, 0, 1024*6))

                hasNew = false
            }

            for i:= 0;i<3; i++ {
                if dataFloat, err = strconv.ParseFloat(other_items[i], 32); err != nil{
                    return
                }
                m.V[m.CountObj -1] = append(m.V[m.CountObj -1], float32(dataFloat))
            }

        case "vt":
            other_items := strings.Split(strings.TrimSpace(line[2:]), " ")
            if len(other_items) != 2{
                err = errors.New("vt length error")
                return
            }
            for i:= 0;i<2; i++ {
                if dataFloat, err = strconv.ParseFloat(other_items[i], 32); err != nil{
                    return
                }
                m.Vt[m.CountObj -1] = append(m.Vt[m.CountObj -1], float32(dataFloat))
            }

        case "vn":
            other_items := strings.Split(strings.TrimSpace(line[2:]), " ")
            if len(other_items) != 3{
                err = errors.New("vn length error")
                return
            }

            for i:= 0;i<3; i++ {
                if dataFloat, err = strconv.ParseFloat(other_items[i], 32); err != nil{
                    return
                }
                m.Vn[m.CountObj -1] = append(m.Vn[m.CountObj -1], float32(dataFloat))
            }
        case "vp":
            other_items := strings.Split(strings.TrimSpace(line[2:]), " ")
            if len(other_items) != 3{
                err = errors.New("vp length error")
                return
            }

            for i:= 0;i<3; i++ {
                if dataFloat, err = strconv.ParseFloat(other_items[i], 32); err != nil{
                    return
                }
                m.Vp[m.CountObj -1] = append(m.Vp[m.CountObj -1], float32(dataFloat))
            }
        case "f":
            hasNew = true
            other_items := strings.Split(strings.TrimSpace(line[1:]), " ")

            if len(other_items)!=4 && len(other_items)!=3{
                err = errors.New("f length error")
                return
            }
            if(len(other_items)==3) {

                if err = m.appendIndex(other_items[1]);err != nil{
                    return
                }


                if err = m.appendIndex(other_items[0]);err != nil{
                    return
                }


                if err = m.appendIndex(other_items[2]);err != nil{
                    return
                }
            }else{

                if err = m.appendIndex(other_items[1]);err != nil{
                    return
                }
                if err = m.appendIndex(other_items[0]);err != nil{
                    return
                }
                if err = m.appendIndex(other_items[2]);err != nil{
                    return
                }
                if err = m.appendIndex(other_items[2]);err != nil{
                    return
                }
                if err = m.appendIndex(other_items[0]);err != nil{
                    return
                }

                if err = m.appendIndex(other_items[3]);err != nil{
                    return
                }


            }
        }
    }

    return
}

func (m *Obj3D)LoadFile(filePath string) (err error){

    //解析文件中的各种数据
    if err = m.parseData(filePath);err != nil{
        return
    }
    log.Println(strconv.Itoa(m.CountObj)+" device count")
    for i:= 0;i < m.CountObj;i++{
        log.Println(strconv.Itoa(i)+" device")
        log.Println("v "+strconv.Itoa(len(m.V[i]))+"")
        log.Println("vt "+strconv.Itoa(len(m.Vt[i]))+"")
        log.Println("vp "+strconv.Itoa(len(m.Vp[i]))+"")
        log.Println("vn "+strconv.Itoa(len(m.Vn[i]))+"")
        log.Println("fv "+strconv.Itoa(len(m.FVIndex[i]))+"")
        log.Println("fn "+strconv.Itoa(len(m.FNIndex[i]))+"")
        log.Println("fuv "+strconv.Itoa(len(m.FUVIndex[i]))+"")

    }
    return

}
